#lang racket

(provide stream?
         the-empty-stream
         stream-car
         stream-cdr
         stream-null?
         cons-stream
         stream-map
         stream-ref
         stream-for-each
         stream-filter
         display-stream
         stream-enumerate-interval
         stream-take
         stream-drop
         stream)

(define (stream? s)
  (cond ((eq? s the-empty-stream) true)
        ((and (pair? s) (promise? (cdr s))) true)
        (else false)))

(define the-empty-stream 'empty-stream)

;(define (stream-car p)
;  (if (pair? p)
;      (car p)
;      p))

(define stream-car car)

(define (stream-cdr stream) (force (cdr stream)))

(define (stream-null? s)
  (eq? s the-empty-stream))

(define-syntax cons-stream
  (syntax-rules ()
    [(cons-stream x y) (cons x (delay y))]))

(define (stream-map proc . argstreams)
  (if (stream-null? (car argstreams))
      the-empty-stream
      (cons-stream
       (apply proc (map stream-car argstreams))
       (apply stream-map
              (cons proc (map stream-cdr argstreams))))))

(define (stream-ref s n)
  (if (= n 0)
      (stream-car s)
      (stream-ref (stream-cdr s) (- n 1))))

(define (stream-for-each proc s)
  (if (stream-null? s)
      'done
      (begin (proc (stream-car s))
             (stream-for-each proc (stream-cdr s)))))

(define (stream-filter pred stream)
  (cond ((stream-null? stream) the-empty-stream)
        ((pred (stream-car stream))
         (cons-stream (stream-car stream)
                      (stream-filter pred
                                     (stream-cdr stream))))
        (else (stream-filter pred (stream-cdr stream)))))

(define (display-x x)
  (display " ")
  (display x))

(define (display-stream s)
  (begin
    (display "[stream")
    (stream-for-each display-x s)
    (display "]\n")))

(define (stream-enumerate-interval low high)
  (if (> low high)
      the-empty-stream
      (cons-stream
       low
       (stream-enumerate-interval (+ low 1) high))))

(define (stream-take n stream)
  (if (= n 0)
      the-empty-stream
      (cons-stream
       (stream-car stream)
       (stream-take (- n 1) (stream-cdr stream)))))

(define (stream-drop n stream)
  (if (= n 0)
      stream
      (stream-drop (- n 1) (stream-cdr stream))))

(define (stream . args)
  (if (null? args)
      the-empty-stream
      (cons-stream (car args) (apply stream (cdr args)))))